class Vector2d
{
    public static EPSILOM:number = 0.00001;
    public values: Float32Array;

    public constructor(x : number = 0, y : number = 0)
    {
        this.values = new Float32Array([x, y]);
    }
    /**
     * 构建向量
     * @param x 
     * @param y 
     */
    public static create(x : number = 0, y : number = 0):Vector2d
    {
        return new Vector2d(x, y);
    }
    /**
     * 向量复制
     * @param from 
     * @param to 
     */
    public static copy(from:Vector2d, to:Vector2d | null = null):Vector2d
    {
        if(to === null)
        {
            to = new Vector2d();
        }
        to.values[0] = from.values[0];
        to.values[1] = from.values[1];
        return to;
    }

    /**
     * 向量相加
     * @param left 
     * @param right 
     * @param result 
     */
    public static sum(left : Vector2d, right : Vector2d, result : Vector2d | null = null) : Vector2d
    {
        if(result === null)
        {
            result = new Vector2d();
        }
        result.values[0] = left.values[0] + right.values[0];
        result.values[1] = left.values[1] + right.values[1];
        return result;
    }

    /**
     * 向量相减
     * @param end 
     * @param start 
     * @param result 
     */
    public static difference( end:Vector2d, start:Vector2d, result:Vector2d | null = null): Vector2d
    {
        if(result === null)
        {
            result = new Vector2d();
        }

        result.values[0] = end.values[0] - start.values[0];
        result.values[1] = end.values[1] - start.values[1];
        return result;
    }

    public static scale(direction : Vector2d, scalar: number, result : Vector2d | null = null): Vector2d
    {
        if(result === null)
        {
            result = new Vector2d();
        }
        result.values[0] = direction.values[0] * scalar;
        result.values[1] = direction.values[1] * scalar;
        return result;
    }

    /**
     * result = start + direction X scalar; 
     * 作用是将一个点（start），沿着direction 给定的方向移动scalar个单位
     * @param start 
     * @param direction 
     * @param scalar 
     * @param result 
     */
    public static scaleAdd(start: Vector2d, direction: Vector2d, scalar:number, result: Vector2d | null = null):Vector2d
    {
        if(result === null)
        {
            result = new Vector2d();
        }
        Vector2d.scale(direction, scalar, result);
        return Vector2d.sum(start, result, result);
    }

    public static dot(left : Vector2d, right : Vector2d): number
    {
        return left.values[0] * right.values[0] + left.values[1] * right.values[1];
    }
    /**
     * 叉乘可以算出两个向量形成的四边形的面积。<br/>
     * @param left 
     * @param right 
     */
    public static cross(left : Vector2d, right : Vector2d) : number
    {
        return left.values[0] * right.values[1] - left.values[1] * right.values[0];
    }

    public get x():number
    {
        return this.values[0];
    }

    public set x(value:number)
    {
        this.values[0] = value;
    }

    public get y():number
    {
        return this.values[1];
    }

    public set y(value:number)
    {
        this.values[1] = value;
    }

    public get squaredLength():number
    {
        let x:number = this.values[0];
        let y:number = this.values[1];
        return (x * x + y * y);
    }

    public get length():number
    {
        return Math.sqrt( this.squaredLength );
    }

    /**
     * 标量
     */
    public normalize():Vector2d
    {
        let len:number = this.length;
        if(Math.abs(len - 0) <= Vector2d.EPSILOM)
        {
            this.values[0] = 0;
            this.values[1] = 0;
            return this;
        }

        if(Math.abs(len - 1) <= Vector2d.EPSILOM)
        {
            return this;
        }
        this.values[0] /= len;
        this.values[1] /= len;
        return this;
    }

    public reset(x:number = 0, y:number = 0):Vector2d
    {
        this.values[0] = x;
        this.values[1] = y;
        
        return this;
    }

    public add(vec:Vector2d):Vector2d
    {
        return Vector2d.sum(this, vec, this);
    }

    public substract(another:Vector2d):Vector2d
    {
        Vector2d.difference(this, another, this);
        return this;
    }

    public negative():Vector2d
    {
        this.values[0] = -this.values[0];
        this.values[1] = -this.values[1];
        return this;
    }

    public equals(vec:Vector2d):boolean
    {
        if(Math.abs(this.values[0] - vec.values[0]) > Vector2d.EPSILOM)
        {
            return false;
        }
        if(Math.abs(this.values[1] - vec.values[1]) > Vector2d.EPSILOM)
        {
            return false;
        }
        return true;
    }

    /**
     * 点乘也称为点积或内积。   
     * 点乘的几何意义是可以用来表征或计算两个向量之间的夹角，以及在b向量在a向量方向上的投影     
     * a . b = |a|.|b|.cosQ     
     * cosQ = a . b / |a|.|b|   
     * 根据这个公式就可以计算向量a和向量b之间的夹角。从而就可以进一步判断这两个向量是否是同一方向，是否正交(也就是垂直)等方向关系，具体对应关系为： 
     * a·b>0    方向基本相同，夹角在0°到90°之间     
     * a·b=0    正交，相互垂直      
     * a·b<0    方向基本相反，夹角在90°到180°之间   
     * @param right 
     */
    public innerProduct(right : Vector2d):number
    {
        return Vector2d.dot(this, right);
    }
    /**
     * 返回一个弧度或角度值
     * @param vec 
     * @param isRadian 值为true时返回角度值，值为false时返回弧度值，默认为false
     */
    public angle(vec:Vector2d, isRadian:boolean = false):number
    {
        let thisLen:number = this.length;
        let vLen:number = this.length;
        if(thisLen == 0 || vLen == 0)
        {
            return 0;
        }
        let dot:number = this.innerProduct(vec);
        let angle:number = Math.acos(dot / (this.length * vec.length));//弧度
        if(isRadian)
        {
            angle = 180 * angle / Math.PI;
        }
        return angle;
    }
    /**
     * 叉乘可以算出两个向量形成的四边形的面积。     
     * 在三维几何中，向量a和向量b的叉乘结果是一个向量，更为熟知的叫法是法向量，该向量垂直于a和b向量构成的平面       
     * 在3D图像学中，叉乘的概念非常有用，可以通过两个向量的叉乘，生成第三个垂直于a，b的法向量，从而构建X、Y、Z坐标系。      
     * 模长：（在这里θ表示两向量之间的夹角（共起点的前提下）（0°≤θ≤180°），它位于这两个矢量所定义的平面上。）       
     * 方向：a向量与b向量的向量积的方向与这两个向量所在平面垂直，且遵守右手定则。（一个简单的确定满足“右手定则”的结果向量的方向的方法是这样的：若坐标系是满足右手定则的，当右手的四指从a以不超过180度的转角转向b时，竖起的大拇指指向是c的方向。）
     * 也可以这样定义（等效）：     
     * 向量积|c|=|a×b|=|a||b|sin<a，b>      
     * 即c的长度在数值上等于以a，b，夹角为θ组成的平行四边形的面积。     
     * 而c的方向垂直于a与b所决定的平面，c的指向按右手定则从a转向b来确定。   
     * 运算结果c是一个伪向量。这是因为在不同的坐标系中c可能不同。 [1]   
     * https://baike.baidu.com/item/%E5%90%91%E9%87%8F%E7%A7%AF/4601007?fr=aladdin
     * @param right 
     */
    public product(right : Vector2d):number
    {
        return Vector2d.cross(this, right);
    }

    public toString():string
    {
        return `[ x:${this.values[0]} , y:${this.values[1]}]`
    }
}

let vec:Vector2d = new Vector2d(20.1, 10);
console.log(vec.normalize())